"""
  cfginit.py, ver 2.2; Phase II
  Ver: 2.1.0; Rev Last: 08/28/2012, Previous: 08/22/2012 (AEM)

  Rev 2.2.0; Last: 12/24/2012 - Previous: 12/21/2012 (AEM)
    - Add config values for get stream processing
    - Add get *.pls file values dialog box.
    - Add parsing of *.pls files and save to cfg file.
    - Add About dialog box.

  Ver 2.1.1; Last 01/25/2213, Previous: 01/25/2013 (AEM)
    - Add function to read in last operating state.

   Support functions for getnetstream.py (now gns.py)
   Adapted from showtags cfginit.py

  Copyright (C) 2010-2013  Arnold E. Morgan

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""

import sys
#import os
#import datetime
import logging
import wx
import wx.lib.mixins.listctrl as listmix
import cPickle

CfgFileName = "showstream.cfg"


# ********** Function Definitions **********
def get_cfg_settings(myStateFile):
   """ Read in showtags config settings.
   Config file MUST be named 'showstream.cfg'.

   Config values are name/value pairs separated with
   an '=' symbol in the form of "key=value".

   First character of '#' signifies a comment line or disabled config value.
   Line will be skipped.

   Also get the last operating state values 
   if they exist. Add them to the ConfigValues array.

   Return values as a three value tuple;
    - 1st value: rtn state: 0 = fail, 1 = success
    - 2nd value: return message
    - 3rd value: cfg data as a dictionary
   """

   RtnState = 0
   RtnMssg = ""
   RtnValue = (RtnState, RtnMssg, '')
   ConfigValues = {}
   ConfigValues['StreamNames'] = ''
   ConfigValues['StreamURLs'] = ''
   ConfigValues['StreamLocs'] = ''
   ConfigValues['StreamRipLoc'] = ''
   ConfigValues['LastState'] = ['','']
   StreamNames = []
   StreamURLs = []
   StreamLocs = []

   ConfigValues['LastState'] = get_last_state(myStateFile)

   logging.info("Ready to open and read config file...")
   try:
      for line in open(CfgFileName, 'r'):
         TempStr = line.strip()
         if len(TempStr) > 0 and TempStr[0] != "#":
            (theKey, theValue) = TempStr.split("=")
            if theKey == "StreamName":
               StreamNames.append(theValue)
            elif theKey == "StreamURL":
               StreamURLs.append(theValue)
            elif theKey == "StreamLoc":
               StreamLocs.append(theValue)
            else:
               ConfigValues[theKey.strip()] = theValue.strip()

      RtnState = 1

   except IOError, (errno, strerror):
      RtnMssg = "Config file open error(%s): %s" % (errno, strerror)
   except:
      RtnMssg = "Unexpected config file open error: ", sys.exc_info()[0]
   finally:
      #RtnMssg += "\nConfig file settings read complete"
      pass

   #*** All of these must exist to execute a valid stream ***
   if len(StreamNames) > 0 and len(StreamNames) == len(StreamURLs) \
                           and len(StreamNames) == len(StreamLocs):
      ConfigValues['StreamNames'] = StreamNames
      ConfigValues['StreamURLs'] = StreamURLs
      ConfigValues['StreamLocs'] = StreamLocs

   RtnValue = (RtnState, RtnMssg, ConfigValues)
   return RtnValue
# *************** End of fcn get_cfg_settings ************

def get_last_state(LastStateFile):
   """ Bring in operating state values from 
   last shutdown.
   
   The calling routines will figure out what to do
   with the values. If any errors on open or read,
   eat it silently. This function is not critical.
   """

   StateVals = ['','']
   StateMssg = ""
   try:
      StateFile = open(LastStateFile, 'r')
      StateVals = cPickle.load(StateFile)
      StateFile.close()
   except IOError, (errno, strerror):
      StateMssg = "State file open error(%s): %s" % (errno, strerror)
      #print StateMssg
   except:
      StateMssg = "Unexpected state file open error: ", sys.exc_info()[0]
      #print StateMssg
   finally:
      pass

   return StateVals

# **********************************************
# *** DESIGN NOTE: Add fcn: CheckInitValues(...) to
# ***              - validate integer values
# ***              - Confirm all values supplied
# **********************************************


# ***********************************************************************
class EditableListCtrl(wx.ListCtrl, listmix.TextEditMixin):
   """ TextEditMixin allows any column to be edited.
   Adapted from http://css.dzone.com/articles/wxpython-wxlistctrl-tips-and
   """

   #----------------------------------------------------------------------
   def __init__(self, parent, ID=wx.ID_ANY, pos=wx.DefaultPosition,
                size=wx.DefaultSize, style=0):
      """ Constructor """
      #wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
      wx.ListCtrl.__init__(self, parent, -1, pos=(30, 50),
                           size=(935, 265),
                           style=wx.LC_REPORT | wx.BORDER_SUNKEN |
                           wx.LC_HRULES)

      listmix.TextEditMixin.__init__(self)
# ***********************************************************************


class GetNewStreamsDialog(wx.Dialog):
   """ Functionality to extract stream definitions from a .pls file
   and add that stream info to the cfg file and the runtime structures.
   Adapted from http://zetcode.com/wxpython/dialogs/
   """

   def __init__(self, parent, id, title):
      wx.Dialog.__init__(self, parent, id, title, size=(1000, 600))

      self.PLSDirName = ""
      self.init_UI()
      #self.SetSize((450, 400))
      self.SetTitle(title)
      self.DefMusicDir = ""

   def init_UI(self):
      MainDialogPanel = wx.Panel(self, -1)
      MainDialogPanel.SetBackgroundColour("#2CB7F3")

      MainSB = wx.StaticBox(MainDialogPanel, wx.ID_ANY,
                            label="  New Streams  ", pos=wx.Point(20, 20),
                            size=wx.Size(960, 560), style=0)

      StreamCtrlsizer = wx.StaticBoxSizer(MainSB, wx.VERTICAL)

      self.StreamDict = {}
      self.StreamDict['NewNames'] = ""
      self.StreamDict['NewURLs'] = ""
      self.StreamDict['NewLocs'] = ""

      # ---------------------------------------------------------------------
      #self.NewStreamListCtrl = wx.ListCtrl(MainDialogPanel, -1, pos=(30,50),
      #      size=(935,265),
      #      style=wx.LC_REPORT | wx.BORDER_SUNKEN | wx.LC_HRULES)

      #self.NewStreamListCtrl = EditableListCtrl(MainDialogPanel,
      #                                          style=wx.LC_REPORT)
      self.NewStreamListCtrl = EditableListCtrl(MainDialogPanel)
      #self.NewStreamListCtrl = EditableListCtrl(self, style=wx.LC_REPORT)
      # --------------------------------------------------------------------

      #*** List of tuple column assignments ***
      ColData = [(0, 435, "Name"), (1, 300, "Stream Source"),
                 (2, 200, "Save Location")]

      self.NewStreamListCtrl.SetFont(wx.Font(8, wx.SWISS, wx.NORMAL,
                                             wx.NORMAL))

      for (ColIndex, ColWidth, ColTitle) in ColData:
         self.NewStreamListCtrl.InsertColumn(ColIndex, ColTitle)
         self.NewStreamListCtrl.SetColumnWidth(ColIndex, ColWidth)
      # --------------------------------------------------------------------

      FileText = wx.StaticText(MainDialogPanel, wx.ID_ANY,
                               'Play List File', (325, 505))

      self.FilePathBox = wx.TextCtrl(MainDialogPanel, wx.ID_ANY,
                                     value="", pos=(420, 500), size=(450, 24))

      OpenFileButton = wx.Button(MainDialogPanel, wx.ID_ANY, label='Get File',
                                 pos=wx.Point(880, 500))

      SaveButton = wx.Button(MainDialogPanel, wx.ID_ANY, label='Save Streams',
                             pos=wx.Point(60, 500))

      self.StatusText = wx.StaticText(MainDialogPanel, wx.ID_ANY,
                                      '', (60, 540))

      DoneButton = wx.Button(MainDialogPanel, wx.ID_OK, label='Done',
                             pos=wx.Point(760, 540))

      CancelButton = wx.Button(MainDialogPanel, wx.ID_CANCEL, label='Cancel',
                               pos=wx.Point(880, 540))

      # ------------- Class Event Methods -------------------
      OpenFileButton.Bind(wx.EVT_BUTTON, self.find_PLS_file)
      SaveButton.Bind(wx.EVT_BUTTON, self.save_stream_vals)
      # -----------------------------------------------------

      StreamCtrlsizer.Add(self.NewStreamListCtrl)
      StreamCtrlsizer.Add(FileText)
      StreamCtrlsizer.Add(self.FilePathBox)
      StreamCtrlsizer.Add(OpenFileButton)
      StreamCtrlsizer.Add(SaveButton)
      StreamCtrlsizer.Add(self.StatusText)
      StreamCtrlsizer.Add(DoneButton)
      StreamCtrlsizer.Add(CancelButton)
   # ********* End of init_UI *********

   def on_close(self, event):
      """ No longer used """
      self.Close()
      #self.Destroy()  *** Not yet, let caller do that ***

   def get_response(self):
      """ Return stream session values initially parsed from the cfg file
      so the caller can update all runtime structures.
      """

      return (self.StreamDict['NewNames'], self.StreamDict['NewURLs'],
              self.StreamDict['NewLocs'])

   def find_PLS_file(self, event):
      """ File Open dialog to locate and open and playlist file. """

      DoneFlag = False
      self.set_info_mssg("", "info")
      dlg = wx.FileDialog(self, "Choose a play list file", self.PLSDirName, "",
                          "*.pls", wx.OPEN)

      if dlg.ShowModal() == wx.ID_OK:
         PLSFileName = dlg.GetFilename()
         self.PLSDirName = dlg.GetDirectory()
         PLSPath = dlg.GetPath()
      else:
         DoneFlag = True
      dlg.Destroy()

      if not DoneFlag and len(PLSPath) > 0:
         self.FilePathBox.SetValue(PLSPath)
         self.set_PLS_values(PLSPath)

   def set_PLS_values(self, PathName):
      """ Populate the available streams list (name=self.NewStreamListCtrl)
      with the stream names and urls from the file.
      """

      (RtnStat, RtnMssg, UserMssg, InitValues) = self.read_file_vals(PathName)

      if not RtnStat:
         Mssg = "PLS file read error \nRtnMssg: %s\n\nUMssg: %s" % \
                (RtnMssg, UserMssg)
         wx.MessageBox(Mssg, 'Info', wx.OK | wx.ICON_ERROR)

      else:
         #*** Now populate the listctrl ***
         #*** Like SetSongListContent() ***
         self.set_stream_list_content(InitValues['StreamNames'],
                                      InitValues['StreamURLs'])

   def read_file_vals(self, PathName):
      """ Loop thru the file and return all instances of
      Filen=... and Titlen=... to the caller.

      Pass back to the caller a list of:
       - RtnStat: 0 = fail, 1 = success
       - RtnMssg: If anything weird happend when opening or reading the file
       - UserMssg: If the names and urls lists do not contain the same item cnt
       - StreamValues dictionary containing:
          - StreamValues['StreamNames'] = list of stream names
          - StreamValues['StreamURLs'] = list of stream urls
      """

      RtnState = 0
      RtnMssg = "OK"
      UserMssg = "ok"
      RtnValue = (RtnState, RtnMssg, UserMssg, '')
      StreamNames = []
      StreamURLs = []
      StreamValues = {}
      StreamValues['StreamNames'] = ''
      StreamValues['StreamURLs'] = ''

      logging.info("Ready to open and read pls file...")
      try:
         for line in open(PathName, 'r'):
            TempLine = line.strip()
            if len(TempLine) > 0 and TempLine.find('=') != -1:
               (theKey, theValue) = TempLine.split("=")
               if theKey[0:4] == "File":
                  StreamURLs.append(theValue)
               elif theKey[0:5] == "Title":
                  StreamNames.append(theValue)
               else:
                  pass

         RtnState = 1     # If no oop-de-doos at this point, all is ok

      except IOError, (errno, strerror):
         RtnMssg = "pls file open error(%s): %s" % (errno, strerror)
      except:
         RtnMssg = "Unexpected pls file open error: ", sys.exc_info()[0]
      finally:
         #RtnMssg += "\npls file settings read complete"
         pass

      #*** All of these must exist to execute a valid stream ***
      if len(StreamNames) > 0 and len(StreamNames) == len(StreamURLs):
         StreamValues['StreamNames'] = StreamNames
         StreamValues['StreamURLs'] = StreamURLs
      else:
         RtnState = 0
         NamesLen = len(StreamNames)
         URLsLen = len(StreamURLs)
         UserMssg = "\nNumber of Stream Names does not equal number of " + \
                    "StreamURLs" + \
                    "\nName Cnt: " + str(NamesLen) + \
                    "\nURL Cnt: " + str(URLsLen)

      RtnValue = (RtnState, RtnMssg, UserMssg, StreamValues)
      return RtnValue
   # *************** End of fcn read_file_vals ************

   def set_stream_list_content(self, StreamNameVals, StreamURLVals):
      """ Populate self.NewStreamListCtrl with URLs and names,
      Stream file Loc will be updated at run time.

      The previous file read routine will insure that both
      passed in lists have the same number of elements.
      """

      IndexCnt = 0
      # If the listctrl has items already, clear it out
      # so we can start fresh.
      if self.NewStreamListCtrl.GetItemCount() > 0:
         self.NewStreamListCtrl.DeleteAllItems()

      for theName in StreamNameVals:
         self.NewStreamListCtrl.InsertStringItem(IndexCnt, theName)
         self.NewStreamListCtrl.SetStringItem(IndexCnt, 1,
                                              StreamURLVals[IndexCnt])
         self.NewStreamListCtrl.SetStringItem(IndexCnt, 2, self.DefMusicDir)
         IndexCnt = IndexCnt + 1

   def save_stream_vals(self, event):
      """ Save selected values from NewStreamListCtrl to the cfg file.

      class data structures and the select combo box will be updated
      by the caller.
      """

      SelectedItemList = self.get_selected_vals()
      if len(SelectedItemList) == 0:
         Mssg = "No values were selected to save"
         wx.MessageBox(Mssg, 'Warning', wx.OK | wx.ICON_EXCLAMATION)
      else:
         self.parse_selected_vals(SelectedItemList)
         self.save_new_cfg_streams()

   def get_selected_vals(self):
      """ Get the list of selected indexes from the NewStreamListCtrl.

      If selections made, return the list of indexes.
      If no selections made, return an empty list.
      Caller will determine the proper action if no selections.
      """

      ItemList = []
      IndexItem = self.NewStreamListCtrl.GetFirstSelected()
      if IndexItem != -1:
         ItemList.append(IndexItem)

      while IndexItem != -1:
         IndexItem = self.NewStreamListCtrl.GetNextSelected(IndexItem)
         if IndexItem != -1:
            ItemList.append(IndexItem)

      return ItemList

   def parse_selected_vals(self, IndexList):
      """ With the passed in list of selected indexes from the listctrl,
      retrieve the name, url and location field values.

      Another function will figure out what to do with them.
      """

      StreamNames = []
      StreamURLs = []
      StreamLocs = []
      StrIndexItems = ""
      TempLoc = ""

      for Indexer in IndexList:
         StrIndexItems += str(Indexer) + ","
         StreamNames.append(self.NewStreamListCtrl.GetItem(Indexer,
                                                           0).GetText())
         StreamURLs.append(self.NewStreamListCtrl.GetItem(Indexer,
                                                          1).GetText())

         TempLoc = self.NewStreamListCtrl.GetItem(Indexer, 2).GetText()
         if len(TempLoc) == 0:
            StreamLocs.append(self.DefMusicDir)
         else:
            StreamLocs.append(TempLoc)

      self.StreamDict['NewNames'] = StreamNames
      self.StreamDict['NewURLs'] = StreamURLs
      self.StreamDict['NewLocs'] = StreamLocs

   def save_new_cfg_streams(self):
      """ Save selected values to the cfg file. """

      FailFlag = False
      FileErrMssg = ""

      NewNames = self.StreamDict['NewNames']
      NewURLs = self.StreamDict['NewURLs']
      NewLocs = self.StreamDict['NewLocs']
      Indexer = 0

      logging.info("Ready to open and write new pls values to cfg file...")
      try:
         cfgfile = open('showstream.cfg', 'a')

         #cfgfile.write('\n')
         for OneName in NewNames:
            cfgfile.write('StreamName=' + OneName + '\n')
            cfgfile.write('StreamURL=' + NewURLs[Indexer] + '\n')
            cfgfile.write('StreamLoc=' + NewLocs[Indexer] + '\n\n')
            Indexer += 1

      except IOError, (errno, strerror):
         FailFlag = True
         FileErrMssg = "pls file open error(%s): %s" % (errno, strerror)
         logging.info(FileErrMssg)
      except:
         FailFlag = True
         FileErrMssg = "Unexpected pls file error: ", sys.exc_info()[0]
         logging.info(FileErrMssg)
      finally:
         #RtnMssg += "\npls file settings write complete"
         pass

      if FailFlag:
         self.set_info_mssg(FileErrMssg, "error")
      else:
         self.set_info_mssg("Save OK", "info")

      cfgfile.close()

   def set_info_mssg(self, theMssgText="Ok", MssgType="info"):
      """ Interface for placing messages in the general purpose
      status text ctrl.
      """

      self.StatusText.SetLabel(theMssgText)
      self.StatusText.Wrap(350)
      #self.StatusText.SetForegroundColour('red')

      if MssgType == "error":
         self.StatusText.SetForegroundColour('red')
      elif MssgType == "info":
         self.StatusText.SetForegroundColour(('black'))
      else:
         self.StatusText.SetForegroundColour('black')


def OnAboutBox():
   """ About dialog box.

   Example from http://zetcode.com/wxpython/dialogs/
   """

   description = "Get Net Stream is a GUI-based application for the " \
                 "Linux Gnome destop environment that captures " \
                 "SHOUTcast compatible Internet radio broadcast streams " \
                 "to your local system for your own personal use and " \
                 "enjoyment."

   licence = """Get Net Stream is free software; you can redistribute 
it and/or modify it under the terms of the GNU General Public License as 
published by the Free Software Foundation; either version 3 of the License, 
or (at your option) any later version.

Get Net Stream is distributed in the hope that it will be useful, 
but WITHOUT ANY WARRANTY; without even the implied warranty of 
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License 
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

   info = wx.AboutDialogInfo()

   info.SetIcon(wx.Icon('gnsimage1.png', wx.BITMAP_TYPE_PNG))
   info.SetName('Get Net Stream')
   info.SetVersion('1.0.4 (Beta)')
   info.SetDescription(description)
   info.SetCopyright('(C) 2012 - 2013 Arnold Morgan')
   info.SetWebSite(('http://www.byartandbylabor.com', 'ByArtAndByLabor.com'))
   info.SetLicence(licence)
   info.AddDeveloper('Arnold Morgan')
   info.AddDocWriter('Arnold Morgan')
   #info.AddArtist('Arnold Morgan')
   #info.Developers = ["Arnold Morgan"]
   info.SetArtists(['Arnold Morgan','and others'])
   #info.AddTranslator('Arnold Morgan')

   wx.AboutBox(info)
